package fr.imag.ufrima.ima.project.core.operations;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import org.apache.log4j.Logger;

import fr.imag.ufrima.ima.project.core.Image;
import fr.imag.ufrima.ima.project.core.ImageOperation;

public class Resize extends ImageOperation {
	
	private int newWidth;
	private int newHeight;

	private float radioX;
	private float radioY;
	
	private TypeResize typeResize;
	
	public Resize(Image originalImage) {
		super(originalImage);
		// TODO Auto-generated constructor stub
	}

	public BufferedImage doOperationJava() {

		logger.debug(String.format("Serving resize image for: %d %d", newWidth, newHeight));
		long startTime = System.currentTimeMillis();

		BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, originalImage.getBufferedImage().getType());
		Graphics2D g = scaledImage.createGraphics();
		g.drawImage(
				originalImage.getBufferedImage().getScaledInstance(newWidth, newHeight, BufferedImage.SCALE_SMOOTH), 0,
				0, null);

		long elapsedTime = System.currentTimeMillis() - startTime;
		logger.debug(String.format("Finishing resize image. Time elapsed: %d ms", elapsedTime));

		return scaledImage;
	}

	public BufferedImage doOperation() {
		BufferedImage result = null;
		
		logger.debug(String.format("Serving resize image type: %s for: %d %d", typeResize, newWidth, newHeight));
		long startTime = System.currentTimeMillis();
		
		switch (this.typeResize) {
			case CONSTANT: result = doConstantInterpolation();
			case LINEAR: result = doLinearInterpolation();
		}

		long elapsedTime = System.currentTimeMillis() - startTime;
		logger.debug(String.format("Finishing resize image. Time elapsed: %d ms", elapsedTime));
		
		return result;		
	}

	private BufferedImage doConstantInterpolation() {
		BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, originalImage.getBufferedImage().getType());

		for (int i=0; i < newWidth; i++) {
			for (int j=0; j< newHeight; j++) {
				int color = this.getResizeColorConstantInterpolation(i, j);
				scaledImage.setRGB(i, j, color);
			}
		}
		
		return scaledImage;
	}

	private int getResizeColorConstantInterpolation(int i, int j) {
		// TODO Auto-generated method stub
		float factorX = (float) i / (float) newWidth;
		float factorY = (float) j / (float) newHeight;
		
		float posX = factorX * originalImage.getWidth();
		float posY = factorY * originalImage.getHeight();
		
		int color = originalImage.getBufferedImage().getRGB((int) posX, (int) posY);
		
		return color;
	}
	
	private BufferedImage doLinearInterpolation() {
		BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, originalImage.getBufferedImage().getType());

		this.prepareLinearInterpolation();
		for (int i=0; i < newWidth; i++) {
			for (int j=0; j< newHeight; j++) {
				int color = this.getResizeColorLinearInterpolation(i, j);
				scaledImage.setRGB(i, j, color);
			}
		}
		
		return scaledImage;
	}

	private void prepareLinearInterpolation() {
		radioX = (float) newWidth / (float) originalImage.getWidth();
		radioY = (float) newHeight / (float) originalImage.getHeight();
		
		//logger.debug(String.format("radioX: %f, radioY: %f.", radioX, radioY ));
		radioX /= 2;
		radioY /= 2;
	}
	
	private int getResizeColorLinearInterpolation(int i, int j) {
		// TODO Auto-generated method stub
		float factorX = (float) i / (float) newWidth;				
		float factorY = (float) j / (float) newHeight;		

//		logger.debug(String.format("Calculating interpolation for integer points: %d %d.", i, j));
//		logger.debug(String.format("factorX: %f factorY: %f.", factorX, factorY));
		
		float pointX1 = (factorX * originalImage.getWidth()) - (radioX);
		float pointX2 = (factorX * originalImage.getWidth()) + (radioX);
		float pointY1 = (factorY * originalImage.getHeight()) - (radioY); 
		float pointY2 = (factorY * originalImage.getHeight()) + (radioY);

		float pointIX = factorX * originalImage.getWidth();
		float pointIY = factorY * originalImage.getHeight();

		if (pointX1 < 0) { 
			pointX1 = 0;
		}
		if (pointX2 > originalImage.getWidth() - 1) {
			pointX2 = originalImage.getWidth() - 1;
		}
		if (pointY1 < 0) { 
			pointY1 = 0;
		}
		if (pointY2 > originalImage.getHeight() - 1) {
			pointY2 = originalImage.getHeight() - 1;
		}
					
//		logger.debug(String.format("Interpolation point: %f %f. Tne 4 nearest points are: %f %f %f %f.", pointIX, pointIY, pointX1, pointX2, pointY1, pointY2));
		
		Color color1 = new Color(originalImage.getBufferedImage().getRGB((int) pointX1, (int) pointY1));
		Color color2 = new Color(originalImage.getBufferedImage().getRGB((int) pointX2, (int) pointY1));
		Color color3 = new Color(originalImage.getBufferedImage().getRGB((int) pointX1, (int) pointY2));
		Color color4 = new Color(originalImage.getBufferedImage().getRGB((int) pointX2, (int) pointY2));				
		
		int redX1 = color1.getRed();
		int redX2 = color2.getRed();
		int redY1 = color3.getRed();
		int redY2 = color4.getRed();

//		logger.debug(String.format("Red intensities: %d %d %d %d", redX1, redX2, redY1, redY2));
		
		float alpha = (pointIX - pointX1) / (pointX2 - pointX1);
		float interpolationX1red = (1 - alpha) * redX1 + (alpha * redX2);
		float interpolationX2red = (1 - alpha) * redY1 + (alpha * redY2);
		float beta = (pointIY - pointY1) / (pointY2 - pointY1);
		float interpolationYred = (1 - beta) * interpolationX1red + (beta * interpolationX2red);
		
		int blueX1 = color1.getBlue();
		int blueX2 = color2.getBlue();
		int blueY1 = color3.getBlue();
		int blueY2 = color4.getBlue();
		
		float interpolationX1blue = (1 - alpha) * blueX1 + (alpha * blueX2);
		float interpolationX2blue = (1 - alpha) * blueY1 + (alpha * blueY2);
		float interpolationYblue = (1 - beta) * interpolationX1blue + (beta * interpolationX2blue);
		
		int greenX1 = color1.getGreen();
		int greenX2 = color2.getGreen();
		int greenY1 = color3.getGreen();
		int greenY2 = color4.getGreen();
		
		float interpolationX1green = (1 - alpha) * greenX1 + (alpha * greenX2);
		float interpolationX2green = (1 - alpha) * greenY1 + (alpha * greenY2);
		float interpolationYgreen = (1 - beta) * interpolationX1green + (beta * interpolationX2green);
		
//		logger.debug(String.format("X Interpolations: %f %f. Y Interpolation: %f",  interpolationX1red, interpolationX2red, interpolationYred));
		
		int iColor = (((int)interpolationYred) << 16) + (((int)interpolationYgreen) << 8) + (int) interpolationYblue;
				
		return iColor;
	}
	
	public void setNewWidth(int newWidth) {
		this.newWidth = newWidth;
	}

	public void setNewHeight(int newHeight) {
		this.newHeight = newHeight;
	}

	public void setTypeResize(TypeResize typeResize) {
		this.typeResize = typeResize;
	}

}
